﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Seagull.Configuration;
using Seagull.DAL;
using Seagull.Events;
using Seagull.Generic;
using Seagull.Model.Alerts;
using Timer = System.Windows.Forms.Timer;

namespace Seagull.Shell.Views
{
    internal class ReplayController : ChannelFrameSetProvider, IDisposable
    {
        #region PlayState enum

        #endregion

        private readonly PlaySpeed _playSpeed;
        private DateTime _startTime;

        private readonly object _syncRoot = new object();

        private readonly Timer _timer;
        private readonly Action<Action> _invoke;

        private Dictionary<byte, FrameSetReader> _frameSetReaders;

        /// <summary>
        ///     构造一个用于控制播放的 <see cref="ReplayController" /> .
        /// </summary>
        public ReplayController(Timer timer, Action<Action> invoke)
        {
            _timer = timer;
            _invoke = invoke;
            CurrentPlayState = PlayState.Pause;
            _playSpeed = new PlaySpeed();
            timer.Tick += OnTimerTick;
        }

        private volatile PlayState _currentPlayState;

        public PlayState CurrentPlayState
        {
            get { return _currentPlayState; }
            private set { _currentPlayState = value; }
        }

        #region IDisposable Members

        public void Dispose()
        {
            _timer.Tick -= OnTimerTick;
        }

        #endregion

        public PlayState PlayPause()
        {
            switch(CurrentPlayState)
            {
                case PlayState.Play:
                    // 暂停
                    Pause();
                    break;
                case PlayState.Pause:
                    // 播放
                    Play();
                    break;
                default:
                    throw new ArgumentOutOfRangeException("_playState");
            }
            return CurrentPlayState;
        }

        /// <param name="channels"> </param>
        /// <param name="startDate"> </param>
        /// <param name="startHour"> </param>
        public void SetStartSegment(byte[] channels, DateTime startDate, int startHour)
        {
            if(CurrentPlayState == PlayState.Play)
            {
                Pause();
            }
            lock (_syncRoot)
            {
                CloseExistingFrameSetReaders();
                _frameSetReaders = channels.ToDictionary(
                    channel => channel,
                    channel => new FrameSetReader(
                        new PathHelper(Config.Instance.PathConfig.SourceDataFolder,
                            Config.Instance.PathConfig.DataFileExtension),
                        new Segment(channel, startDate, startHour)));
            }
            _startTime = startDate.AddHours(startHour);
        }

        public void SpeedUp()
        {
            _playSpeed.Increase();
            _timer.Interval = _playSpeed.Interval;
        }

        public void SlowDown()
        {
            _playSpeed.Decrease();
            _timer.Interval = _playSpeed.Interval;
        }

        public event EventHandler<EventArgs<PlayState>> PlayStateChanged;

        [DebuggerNonUserCode]
        public void OnPlayStateChanged(PlayState e)
        {
            var handler = PlayStateChanged;
            if(handler != null)
                handler(this, new EventArgs<PlayState>(e));
        }

        public void Play()
        {
            // 不能回放当前小时及将来时间的数据
            if(_startTime >= DateTime.Now.TruncateToHour())
            {
                return;
            }

            // 没有选中通道不能回放
            if(_frameSetReaders.Count == 0)
            {
                return;
            }

            if (CurrentPlayState != PlayState.Play)
            {
                CurrentPlayState = PlayState.Play;
                _timer.Interval = _playSpeed.Interval;
                _timer.Start();
                OnPlayStateChanged(PlayState.Play);
            }
        }

        public void Pause()
        {
            _timer.Stop();
            if (CurrentPlayState != PlayState.Pause)
            {
                CurrentPlayState = PlayState.Pause;
                OnPlayStateChanged(PlayState.Pause);
            }
        }

        private void CloseExistingFrameSetReaders()
        {
            if(_frameSetReaders != null)
            {
                foreach (var frameSetReader in _frameSetReaders.Values)
                {
                    frameSetReader.Dispose();
                }
            }
        }

        private void OnTimerTick(object sender, EventArgs e)
        {
            var channelFrameSetsTask = Task.Factory.StartNew(frameSetJumpingObj =>
            {
                if(Monitor.TryEnter(_syncRoot))
                {
                    var frameSetJumping = (int)frameSetJumpingObj;
                    // 跳帧
                    for(var i = 0; i < frameSetJumping; i++)
                    {
                        foreach(var reader in _frameSetReaders.Values)
                        {
                            var frameSet = reader.Read();
                            if(frameSet == null)
                            {
                                break;
                            }
                        }
                    }
                    // 读取帧
                    return _frameSetReaders
                        .Select(frameSetReader => Tuple.Create(
                            frameSetReader.Key,
                            frameSetReader.Value.Read()))
                        .ToArray();
                }
                else
                {
                    return null;
                }
            }, _playSpeed.FrameSetJumping);

            channelFrameSetsTask.ContinueWith(channelFrameSets =>
            {
                try
                {
                    if (channelFrameSets.Result == null)
                    {
                        return;
                    }

                    if(channelFrameSets.Result.Any(t => t.Item2 == null))
                    {
                        // 播放结束
                        _invoke(Pause);
                        return;
                    }
                    OnNewFrames(new NewFramesEventArgs
                    {
                        ChannelFrames = channelFrameSets.Result,
                    });
                }
                catch
                {
                }
            });

            channelFrameSetsTask.ContinueWith(t =>
            {
                try
                {
                    _invoke(Pause);
                    LogOnlyAlertEvent.Instance.Publish(new ApplicationAlert(t.Exception.InnerException, "回放"));
                }
                catch
                {
                }
            }, TaskContinuationOptions.OnlyOnFaulted);
        }
    }
}